#include "game_transam.h"

using namespace std;

//innitialise a car back to it's defaults
const float Car::FUEL_MAX=60.0;	//60
const float Car::FUEL_INC=1/FUEL_MAX;
void Car::SetupCar()
{
	int carwidth,carheight;
	angle=0;acc=0;vel=0.0;
	decel=0.5;
	car=NULL;
	Handbrake=false;
	Mph=0;
	Fuel=Car::FUEL_MAX;
	Temperature=0.0;
	DamageLevel=32;
	MilesDriven=0.0;
	TemperatureLatch=false;
	BlockBadResponse=BlockTopSpeedIncrease=false;
	SpeedFactor=1.0;

	TemperatureInc=3.333/GameConfiguration->CapsSystem.fps;
	TemperatureDec=5.0/GameConfiguration->CapsSystem.fps;
	FuelUnit=(1.0/(float)Car::MPH_MAX)/GameConfiguration->CapsSystem.fps;
	
	if(car) delete car;		//delete if already exists
	car=new Animation(GameLibrary,"player");	//get graphics for the car
	if(Configuration::GlobalErrorString!="")
		GameFramework->AbortSystem("animation 'player' could not be found for setupcar");

	//get size of car and the positions to show it
	car->GetStandardDimensions(&carwidth, &carheight);
	//car is always in the centre of the screen, so we already know
	//what bit of the map is being shown, so we get the centre of that
	//as it is shown on the screen
	screenx=((ShownMapWidth/2)+ScreenXOffset)-(carwidth/2);
	screeny=((ShownMapHeight/2)+ScreenYOffset)-(carheight/2);
	//what the car is doing
	Move=CAR_FREEWHEELING;
	Direction=CAR_STRAIGHT;
	State=CAR_NORMAL;
}

Car::~Car()
{
	//remove animation
	if(car) delete car;
}

bool Car::NextMove()
{	
	//update the car. called every frame
	//default to no acceleration pressed
	//and if simple acceleration then car will decrease when no acceleration

	acc=0;
		//if(Temperature<75) TemperatureLatch=false;
	if(Temperature>=100 || (TemperatureLatch && Temperature>75))TemperatureLatch=true;
	else TemperatureLatch=false;

	if((TemperatureLatch && Mph>100) || this->Fuel<=0) decel=0.05;
	else decel=0.0;
	Handbrake=false;
	
	//if run out of fuel, let it conk out first
	if(Fuel<=0 && vel>=0 && State!=CAR_DYING)
	{
		//dead
		State=CAR_DYING;
		car->SetNewAnimation("dying");
		car->SetCustomCountDown(1,2000);
	}
	Move=CAR_FREEWHEELING;
	Direction=CAR_STRAIGHT;
	
	//key presses
	//order is important
	if(this->State!=CAR_NORMAL)
	{
		acc=0;
		decel=0.2;
	}
	else
	{
		if(keypressed())
		{
			if(key[KEY_LSHIFT]) Handbrake=true;
			if(key[KEY_ESC])
			{
				clear_keybuf();
				return true;
			}

			//brake takes precedence
			if(key[KEY_DOWN]) 
			{
				//if(decel==0) decel=0.05;
				Move=CAR_DECELERATE;
			}
			else
			{
				//if(!TemperatureLatch) decel=0.0;
				if(key[KEY_UP]) Move=CAR_ACCELERATE;
			}
			if(key[KEY_RIGHT]) Direction=CAR_RIGHT;
			if(key[KEY_LEFT]) Direction=CAR_LEFT;
		}
		
		//handle movement
		if(Move==CAR_ACCELERATE)
			Accelerate();
		else if(Move==CAR_DECELERATE)
			Decelerate();

		//handle direction
		if(Direction==CAR_LEFT)
			TurnLeft();
		else if(Direction==CAR_RIGHT)
			TurnRight();

		//handbrake increases the braking rate and stops any acceleration
		if(Handbrake)
		{
			decel=0.2;
			acc=0;
		}
	}
	//adjust speed
	//if((!this->SimpleAcceleration || TemperatureLatch))
	{
		//due to rounding problems of tiny fractions stop the car if we know it has stopped
		if(acc==0 && vel>=0) 
		{
			decel=0;
			vel=0;
		}
		
		//adjust speed based on acceleration/deceleration
		vel=vel-(acc*0.1)+decel;
	
		//maximum or minimum velocity reached
		if(!this->BlockTopSpeedIncrease)
		{
			//if no topspeed increase then can be capped speed
			if(vel<-((float)Car::MAXVEL*SpeedFactor)) vel=-((float)Car::MAXVEL*SpeedFactor);
			if(vel>((float)Car::MAXVEL*SpeedFactor)) vel=(float)Car::MAXVEL*SpeedFactor;
		}
		else
		{
			if(vel<-Car::MAXVELTURBO) vel=-Car::MAXVELTURBO;
			if(vel>Car::MAXVELTURBO) vel=Car::MAXVELTURBO;
		}
	}

	//set cars statistics
	Mph=(int)fabs((vel*Car::MPH_INCREASE)*SpeedFactor);
	if(MilesDriven<999999.0) MilesDriven+=((float)Mph/10.0)/GameConfiguration->CapsSystem.fps;

	//temperature is 100 units, increase by 3.33 per second when redlining
	//if mph in yellow, temperature stays the same
	//if in green, temperature decreases
	if((Mph)>Car::MPH_REDLINE) 
		Temperature+=TemperatureInc;
	else if((Mph)<Car::MPH_YELLOWLINE) 
		Temperature-=TemperatureDec;

	if(Temperature>=100) Temperature=100;
	if(Temperature<0) Temperature=0;
	//fuel - idling doesn't use up much fuel (i.e. pretend 0.5 mph
	Fuel-=(Mph+0.5)*FuelUnit;
	if(Fuel<0) Fuel=0;

	//check for edge bounces - there must be a better way!
	//left or top 
	if (mapx-(ShownMapWidth/2) < 0) 
	{
		if(angle>270)	//upleft
			angle=360-angle;
		else
			angle=180-(angle-180);
	}
	if (mapy-(ShownMapHeight/2) < 0) 
	{
		if(angle>270)	//upleft
			angle=360+180-angle;
		else
			angle=90+(90-angle);	//downleft
	}
	//right or bottom
	if (mapx+(ShownMapWidth/2) > mapblockwidth*mapwidth-16) 
	{
		if(angle<89)	//upleft
			angle=360-angle;
		else
			angle=180+(180-angle);	//downright
	}
	if (mapy+(ShownMapHeight/2) > mapblockheight*mapheight-1) 
	{
		if(angle<179)	//downright
			angle=180-angle;
		else
			angle=360-(angle-180);//downleft
	}


	//get movement for car, swapped round for our movement
	xdistancemoved=sin((float)(angle*PI_Conv))*vel;
	ydistancemoved=cos((float)(angle*PI_Conv))*vel;

	//set map offset as being that of the player car
	this->mapx-=xdistancemoved;
	this->mapy+=ydistancemoved;

	if(State==CAR_DYING)
	{
		car->NextMove();	//update any animations (only animation is death)
	}
	CheckCollision();	//still bounce if dying

	return false;
}

void Car::CheckCollision()
{
/*
	other1     (bit) : petrol
	other2     (bit) : increased top speed
	other3     (bit) : cup
	topleft	   (bit) : unused
	topright   (bit) : bad responsiveness on car
	bottomleft (bit) : die
	bottomright(bit) : blank space (can place cup here)
	userdata5  (char): topspeed restriction (decreased top speed) as percentage of maxspeed
					   usually used with topright, e.g. mud, water
    userdata6  (char): bounce and damage car, 1 for damage.

   other2 used by road, objects are either topleft or bottomleft
   bottomright checked first to show no other tests are required 

   all the attributes above are mutually exclusive (i.e. if one is find, can return),
   except topright/userdata5
*/
	//using array is faster
	BLKSTR* CurrentBlock=MapGetBlockInPixels (this->mapx,this->mapy);

	//reset everything
	BlockTopSpeedIncrease=false;
	BlockBadResponse=false;
	SpeedFactor=1;

	//check if blank space. as nothing else is there. if a cup is placed there at the start
	//the block is updated
	if(CurrentBlock->br) return;

	//dead
	if(CurrentBlock->bl && State!=CAR_DYING)
	{
		//dead
		State=CAR_DYING;
		car->SetNewAnimation("dying");
		car->SetCustomCountDown(1,2000);
		return;
	}

	//cup
	if(CurrentBlock->unused3)
	{
		MapSetBlockInPixels(mapx,mapy,CupBlockFreeNumber);
		NumCups++;
		return;
	}

	//topspeed increase
	if(CurrentBlock->unused2)
	{
		BlockTopSpeedIncrease=true;
		return;
	}
	
	//petrol
	if(CurrentBlock->unused1 && Fuel<Car::FUEL_MAX) 
	{
		this->Fuel+=Car::FUEL_INC*30;
		return;
	}

	//bounce
	if(CurrentBlock->user6)
	{
		if(angle>180) angle=(angle-180);
		else angle+=180;
		this->Temperature=100;
		DamageLevel-=8;	//starts off at 32 (height of the car)
		if(DamageLevel<1 && State!=CAR_DYING) 
		{
			DamageLevel=0;
			State=CAR_DYING;
			car->SetNewAnimation("dying");
			car->SetCustomCountDown(1,3000);
		}
		return;
	}

	//bad response, need to check userdata5
	if(CurrentBlock->tr)
		BlockBadResponse=true;
	if(CurrentBlock->user5)
	{
		if(CurrentBlock->user5<=100 && CurrentBlock->user5>0)
		{
			SpeedFactor=((float)CurrentBlock->user5/100);
			return;
		}
	}

	//cup
	if(CurrentBlock->unused3)
	{
	}
	return;
}

void Car::Accelerate()
{
	//can accelerate if temperature<max red
	//if temperature reaches 100, must wait until 75 (yellow)
	//car can always go upto 100mph
	if(Fuel && 
		(Mph<100 
		 || (Temperature<100 && !TemperatureLatch) 
		 || (Temperature<75 && TemperatureLatch)
		)
	)
	{
		//if(Temperature<75) TemperatureLatch=false;
		decel=0;
		acc=1;
	}
	else
	{
		//TemperatureLatch=true;
	}
}

void Car::Decelerate()
{
		decel=0.07;
}

void Car::TurnLeft()
{
	static int counter=1;

	counter++;
	if(counter>2)
	{
		angle-=9;
		if(angle<0) 
			angle=360+angle;
		counter=0;
	}
	//if not accelerating and turning then apply a bit of deceleration
	if(acc==0) decel*=2;
}

void Car::TurnRight()
{
	static int counter=1;

	counter++;
	if(counter>2)
	{
		angle+=9;
		if(angle>359) angle=360-angle;
		counter=0;
	}
	//if not accelerating and turning then apply a bit of deceleration
	if(acc==0) decel*=2;	
}
